home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
nethack.lha
/
nethack-3.1
/
src
/
spell.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-13
|
16KB
|
627 lines
/* SCCS Id: @(#)spell.c 3.1 92/12/10
/* Copyright (c) M. Stephenson 1988 */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
static schar NEARDATA delay; /* moves left for this spell */
static struct obj NEARDATA *book; /* last/current book being xscribed */
#define spelluses(spell) spl_book[spell-1].sp_uses
#define decrnuses(spell) spl_book[spell-1].sp_uses--
#define spellev(spell) spl_book[spell-1].sp_lev
#define spellname(spell) OBJ_NAME(objects[spl_book[spell-1].sp_id])
#define spellid(spell) spl_book[spell-1].sp_id
static void FDECL(cursed_book, (int));
static void FDECL(deadbook, (struct obj *));
STATIC_PTR int NDECL(learn);
static int NDECL(getspell);
static char FDECL(spellet, (int));
static char NDECL(dospellmenu);
static void
cursed_book(lev)
register int lev;
{
switch(rn2(lev)) {
case 0:
You("feel a wrenching sensation.");
tele(); /* teleport him */
break;
case 1:
You("feel threatened.");
aggravate();
break;
case 2:
make_blinded(Blinded + rn1(100,250),TRUE);
break;
case 3:
take_gold();
break;
case 4:
pline("These runes were just too much to comprehend.");
make_confused(HConfusion + rn1(7,16),FALSE);
break;
case 5:
pline("The book was coated with contact poison!");
if (uarmg) {
/* Note: at this writing, there are no corrodeable
* gloves in the game. If no one plans on adding
* copper gauntlets, most of this could be removed. -3.
*/
if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {
Your("gloves seem unaffected.");
} else if (uarmg->oeroded < MAX_ERODE) {
Your("gloves corrode%s!",
uarmg->oeroded+1 == MAX_ERODE ? " completely" :
uarmg->oeroded ? " further" : "");
uarmg->oeroded++;
} else
Your("gloves %s completely corroded.",
Blind ? "feel" : "look");
break;
}
if(Poison_resistance) {
losestr(rn1(1,2));
losehp(rnd(6), "contact-poisoned spellbook", KILLED_BY_AN);
} else {
losestr(rn1(4,3));
losehp(rnd(10), "contact-poisoned spellbook", KILLED_BY_AN);
}
break;
case 6:
if(Antimagic) {
shieldeff(u.ux, u.uy);
pline("The book explodes, but you are unharmed!");
} else {
pline("As you read the book, it explodes in your %s!",
body_part(FACE));
losehp (2*rnd(10)+5, "exploding rune", KILLED_BY_AN);
}
break;
default:
rndcurse();
break;
}
return;
}
/* special effects for The Book of the Dead */
static void
deadbook(book2)
struct obj *book2;
{
if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
register struct obj *otmp;
register boolean arti1_primed = FALSE, arti2_primed = FALSE,
arti_cursed = FALSE;
if(book2->cursed) {
pline("The runes appear scrambled. You can't read them!");
return;
}
if(!u.uhave.bell || !u.uhave.menorah) {
pline("A chill runs down your spine.");
if(!u.uhave.bell) You("hear a faint chime...");
if(!u.uhave.menorah) pline("Vlad's doppelganger is amused.");
return;
}
for(otmp = invent; otmp; otmp = otmp->nobj) {
if(otmp->otyp == CANDELABRUM_OF_INVOCATION &&
otmp->spe == 7 && otmp->lamplit) {
if(!otmp->cursed) arti1_primed = TRUE;
else arti_cursed = TRUE;
}
if(otmp->otyp == BELL_OF_OPENING &&
(moves - otmp->age) < 5L) { /* you rang it recently */
if(!otmp->cursed) arti2_primed = TRUE;
else arti_cursed = TRUE;
}
}
if(arti_cursed) {
pline("The invocation fails!");
pline("At least one of your artifacts is cursed...");
} else if(arti1_primed && arti2_primed) {
mkinvokearea();
u.uevent.invoked = 1;
} else { /* at least one artifact not prepared properly */
You("have a feeling that something is amiss...");
goto raise_dead;
}
return;
}
/* when not an invocation situation */
if(book2->cursed)
raise_dead:
{
register struct monst *mtmp;
coord mm;
You("raised the dead!");
mm.x = u.ux;
mm.y = u.uy;
mkundead(&mm);
if(!rn2(4))
if(mtmp = makemon(&mons[PM_MASTER_LICH],u.ux,u.uy)) {
mtmp->mpeaceful = 0;
set_malign(mtmp);
}
} else if(book2->blessed) {
register struct monst *mtmp, *mtmp2;
for(mtmp = fmon; mtmp; mtmp = mtmp2) {
mtmp2 = mtmp->nmon; /* tamedog() changes chain */
if(is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {
mtmp->mpeaceful = TRUE;
if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
&& distu(mtmp->mx, mtmp->my) < 4)
if (mtmp->mtame)
mtmp->mtame++;
else
(void) tamedog(mtmp, (struct obj *)0);
else mtmp->mflee = TRUE;
}
}
} else {
switch(rn2(3)) {
case 0:
Your("ancestors are annoyed with you!");
break;
case 1:
pline("The headstones in the cemetery begin to move!");
break;
default:
pline("Oh my! Your name appears in the book!");
}
}
return;
}
STATIC_PTR
int
learn()
{
register int i;
register unsigned booktype;
if (delay) { /* not if (delay++), so at end delay == 0 */
delay++;
return(1); /* still busy */
}
exercise(A_WIS, TRUE); /* you're studying. */
booktype = book->otyp;
if(booktype == SPE_BOOK_OF_THE_DEAD) {
deadbook(book);
return(0);
}
for (i = 0; i < MAXSPELL; i++) {
if (spl_book[i].sp_id == booktype) {
if (book->spestudied >= rn1(1,8-spl_book[i].sp_lev)) {
pline("This spellbook is too faint to be read anymore.");
book->otyp = booktype = SPE_BLANK_PAPER;
makeknown((int)booktype);
}
else if (spl_book[i].sp_uses < 11-spl_book[i].sp_lev) {
Your("knowledge of that spell is keener.");
spl_book[i].sp_uses += rn1(1,9-spl_book[i].sp_lev);
book->spestudied++;
exercise(A_WIS, TRUE); /* extra study */
} else
You("know that spell quite well already.");
break;
} else if (spl_book[i].sp_id == NO_SPELL) {
spl_book[i].sp_id = booktype;
spl_book[i].sp_lev = objects[booktype].oc_level;
/* spells have 1 .. 9-level uses. */
/* ie 1 or 2 uses w/ most potent */
spl_book[i].sp_uses = rn1(1,9-spl_book[i].sp_lev);
book->spestudied++;
You("add the spell to your repertoire.");
makeknown((int)booktype);
break;
}
}
if (i == MAXSPELL) impossible("Too many spells memorized!");
if (book->cursed) { /* maybe a demon cursed it */
cursed_book(objects[booktype].oc_level);
}
check_unpaid(book);
book = 0;
return(0);
}
int
study_book(spellbook)
register struct obj *spellbook;
{
register int booktype = spellbook->otyp;
register boolean confused = (Confusion != 0);
if (delay && spellbook == book)
You("continue your efforts to memorize the spell.");
else {
switch(booktype) {
/* blank spellbook */
case SPE_BLANK_PAPER:
pline("This spellbook is all blank.");
makeknown(SPE_BLANK_PAPER);
return(1);
/* level 1 spells */
case SPE_HEALING:
case SPE_DETECT_MONSTERS:
case SPE_FORCE_BOLT:
case SPE_LIGHT:
case SPE_SLEEP:
case SPE_KNOCK:
/* level 2 spells */
case SPE_MAGIC_MISSILE:
case SPE_CONFUSE_MONSTER:
case SPE_SLOW_MONSTER:
case SPE_CURE_BLINDNESS:
case SPE_CREATE_MONSTER:
case SPE_DETECT_FOOD:
case SPE_WIZARD_LOCK:
delay = -objects[booktype].oc_delay;
break;
/* level 3 spells */
case SPE_HASTE_SELF:
case SPE_CAUSE_FEAR:
case SPE_CURE_SICKNESS:
case SPE_DETECT_UNSEEN:
case SPE_EXTRA_HEALING:
case SPE_CHARM_MONSTER:
case SPE_CLAIRVOYANCE:
/* level 4 spells */
case SPE_LEVITATION:
case SPE_RESTORE_ABILITY:
case SPE_INVISIBILITY:
case SPE_FIREBALL:
case SPE_DETECT_TREASURE:
delay = -(objects[booktype].oc_level - 1) * objects[booktype].oc_delay;
break;
/* level 5 spells */
case SPE_REMOVE_CURSE:
case SPE_MAGIC_MAPPING:
case SPE_CONE_OF_COLD:
case SPE_IDENTIFY:
case SPE_DIG:
/* level 6 spells */
case SPE_TURN_UNDEAD:
case SPE_POLYMORPH:
case SPE_CREATE_FAMILIAR:
case SPE_TELEPORT_AWAY:
delay = -objects[booktype].oc_level * objects[booktype].oc_delay;
break;
/* level 7 spells */
case SPE_CANCELLATION:
case SPE_FINGER_OF_DEATH:
case SPE_BOOK_OF_THE_DEAD:
delay = -8 * objects[booktype].oc_delay;
break;
/* impossible */
default:
impossible("Unknown spellbook, %d;", booktype);
return(0);
}
/* Books are often wiser than their readers (Rus.) */
if(!spellbook->blessed &&
spellbook->otyp != SPE_BOOK_OF_THE_DEAD &&
(spellbook->cursed ||
rn2(20) > (ACURR(A_INT) + 4 + (int)(u.ulevel/2)
- 2*objects[booktype].oc_level))) {
cursed_book(objects[booktype].oc_level);
nomul(delay); /* study time */
delay = 0;
if(!rn2(3)) {
useup(spellbook);
pline("The spellbook crumbles to dust!");
}
return(1);
}
else if(confused) {
if(!rn2(3) &&
spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
useup(spellbook);
pline("Being confused you have difficulties in controlling your actions.");
display_nhwindow(WIN_MESSAGE, FALSE);
You("accidentally tear the spellbook to pieces.");
}
else
You("find yourself reading the first line over and over again.");
nomul(delay);
delay = 0;
return(1);
}
You("begin to %s the runes.",
spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" :
"memorize");
}
book = spellbook;
set_occupation(learn, "studying", 0);
return(1);
}
static int
getspell()
{
register int maxs, ilet, i;
char lets[BUFSZ], buf[BUFSZ], qbuf[QBUFSZ];
if (spl_book[0].sp_id == NO_SPELL) {
You("don't know any spells right now.");
return(0);
} else {
for(maxs = 1; (maxs < MAXSPELL) && (spl_book[maxs].sp_id != NO_SPELL); maxs++);
if (maxs >= MAXSPELL) {
impossible("Too many spells memorized.");
return(0);
}
for(i = 0; (i < maxs) && (i < 26); buf[++i] = 0) buf[i] = 'a' + i;
for(i = 26; (i < maxs) && (i < 52); buf[++i] = 0) buf[i] = 'A' + i - 26;
if (maxs == 1) Strcpy(lets, "a");
else if (maxs < 27) Sprintf(lets, "a-%c", 'a' + maxs - 1);
else if (maxs == 27) Sprintf(lets, "a-z A");
else Sprintf(lets, "a-z A-%c", 'A' + maxs - 27);
for(;;) {
Sprintf(qbuf, "Cast which spell? [%s ?]", lets);
if ((ilet = yn_function(qbuf, NULL, '\0')) == '?') {
ilet = dospellmenu();
if(!ilet)
continue;
}
if (index(quitchars, ilet))
return(0);
else for(i = 0; buf[i] != 0; i++)
if(ilet == buf[i]) return(++i);
You("don't know that spell.");
}
}
}
int
docast()
{
register int spell;
spell = getspell();
if (!spell) return(0);
return(spelleffects(spell,FALSE));
}
int
spelleffects(spell,atme)
register int spell;
boolean atme;
{
register int energy, damage;
boolean confused = (Confusion != 0);
struct obj *pseudo;
/* note that trying to cast it decrements the # of uses, */
/* even if the mage does not have enough food/energy to use */
/* the spell */
switch (spelluses(spell)) {
case 0:
pline ("Curdled magical energy twists through you...");
pline ("...you have overloaded and burned out this spell.");
make_confused((long)spellev(spell) * 3, FALSE);
return(0);
case 1:
Your("nerves tingle warningly.");
break;
case 2:
pline ("This spell is starting to be over-used.");
break;
default:
break;
}
decrnuses(spell);
energy = spellev(spell) * 7 / 2 - 2; /* 1 <= energy <= 22 */
if (u.uhunger <= 100 && spell != SPE_DETECT_FOOD) {
You("are too hungry to cast that spell.");
return(0);
} else if (ACURR(A_STR) < 6) {
You("lack the strength to cast spells.");
return(0);
} else if(check_capacity(
"Your concentration falters while carrying so much stuff.")) {
return (1);
}
if (u.uhave.amulet) {
You("feel the amulet draining your energy away.");
energy *= rnd(3);
}
if(energy > u.uen) {
You("don't have enough energy to cast that spell.");
return(0);
} else {
if (spell != SPE_DETECT_FOOD)
morehungry(energy * 10);
u.uen -= energy;
}
flags.botl = 1;
if (confused ||
((int)(ACURR(A_INT) + Luck) - 3 * spellev(spell)) < 0) {
if (Hallucination)
pline("Far out... a light show!");
else pline("The air around you crackles as you goof up.");
return(0);
}
exercise(A_WIS, TRUE);
/* pseudo is a temporary "false" object containing the spell stats. */
pseudo = mksobj(spellid(spell), FALSE, FALSE);
pseudo->blessed = pseudo->cursed = 0;
pseudo->quan = 20L; /* do not let useup get it */
switch(pseudo->otyp) {
/* These spells are all duplicates of wand effects */
case SPE_FORCE_BOLT:
case SPE_SLEEP:
case SPE_MAGIC_MISSILE:
case SPE_KNOCK:
case SPE_SLOW_MONSTER:
case SPE_WIZARD_LOCK:
case SPE_FIREBALL:
case SPE_CONE_OF_COLD:
case SPE_DIG:
case SPE_TURN_UNDEAD:
case SPE_POLYMORPH:
case SPE_TELEPORT_AWAY:
case SPE_CANCELLATION:
case SPE_FINGER_OF_DEATH:
case SPE_LIGHT:
case SPE_DETECT_UNSEEN:
if (!(objects[pseudo->otyp].oc_dir == NODIR)) {
if (atme) u.dx = u.dy = u.dz = 0;
else (void) getdir(NULL);
if(!u.dx && !u.dy && !u.dz) {
if((damage = zapyourself(pseudo)))
losehp(damage,
self_pronoun("zapped %sself with a spell", "him"),
NO_KILLER_PREFIX);
} else weffects(pseudo);
} else weffects(pseudo);
break;
/* These are all duplicates of scroll effects */
case SPE_CONFUSE_MONSTER:
case SPE_DETECT_FOOD:
case SPE_CAUSE_FEAR:
case SPE_CHARM_MONSTER:
case SPE_REMOVE_CURSE:
case SPE_MAGIC_MAPPING:
case SPE_CREATE_MONSTER:
case SPE_IDENTIFY:
(void) seffects(pseudo);
break;
case SPE_HASTE_SELF:
case SPE_DETECT_TREASURE:
case SPE_DETECT_MONSTERS:
case SPE_LEVITATION:
case SPE_RESTORE_ABILITY:
case SPE_INVISIBILITY:
(void) peffects(pseudo);
break;
case SPE_HEALING:
You("feel a bit better.");
healup(rnd(8), 0, FALSE, FALSE);
break;
case SPE_CURE_BLINDNESS:
healup(0, 0, FALSE, TRUE);
break;
case SPE_CURE_SICKNESS:
if (Sick) You("are no longer ill.");
healup(0, 0, TRUE, FALSE);
break;
case SPE_EXTRA_HEALING:
You("feel a fair bit better.");
healup(d(2,8)+2, 0, FALSE, FALSE);
break;
case SPE_CREATE_FAMILIAR:
make_familiar((struct obj *)0, u.ux, u.uy);
break;
case SPE_CLAIRVOYANCE:
do_vicinity_map();
break;
default:
impossible("Unknown spell %d attempted.", spell);
obfree(pseudo, (struct obj *)0);
return(0);
}
obfree(pseudo, (struct obj *)0); /* now, get rid of it */
return(1);
}
void
losespells() {
register boolean confused = (Confusion != 0);
register int n, nzap, i;
book = 0;
for(n = 0;(spl_book[n].sp_id != NO_SPELL) && (n < MAXSPELL); n++);
if (!n) return;
if (n < MAXSPELL) {
nzap = rnd(n);
if (nzap < n) nzap += confused;
for (i = 0; i < nzap; i++) {
spl_book[n-i-1].sp_id = NO_SPELL;
exercise(A_WIS, FALSE); /* ouch! */
}
} else impossible("Too many spells memorized!");
return;
}
static char
spellet(spl)
int spl;
{
return (spl < 27) ? ('a' + spl - 1) : ('A' + spl - 27);
}
int
dovspell()
{
(void) dospellmenu();
return 0;
}
static char
dospellmenu()
{
winid tmpwin;
register int maxs, i;
char rval;
char buf[BUFSZ];
if (spl_book[0].sp_id == NO_SPELL) {
You("don't know any spells right now.");
return 0;
}
for(maxs = 1; (maxs < MAXSPELL) && (spl_book[maxs].sp_id != NO_SPELL); maxs++);
if (maxs >= MAXSPELL) {
impossible("Too many spells memorized.");
return 0;
}
tmpwin = create_nhwindow(NHW_MENU);
start_menu(tmpwin);
add_menu(tmpwin, 0, 0, "Currently known spells:");
add_menu(tmpwin, 0, 0, "");
for(i = 1; i <= maxs; i++) {
Sprintf(buf, "%c %c %s (%d)",
spellet(i), (spelluses(i)) ? '-' : '*',
spellname(i), spellev(i));
add_menu(tmpwin, spellet(i), 0, buf);
}
end_menu(tmpwin, '\0', "\033 ", NULL);
rval = select_menu(tmpwin);
destroy_nhwindow(tmpwin);
return rval;
}
/*spell.c*/